#include "mm.h"
#include "string.h"
#include "bug.h"
#include "config.h"
#include "printk.h"
#include "page.h"
#include "error.h"
#include "sched.h"
#include "pagemap.h"

#include "linux/errno.h"
#include "asm/pgtable.h"


mem_map_t *mem_map;

unsigned long max_mapnr;
unsigned long num_physpages;
unsigned long zero_pfn;


/*
 * 设置脏页
 */
void __set_page_dirty(struct page *page)
{
/*
    struct address_space *mapping = page->mapping;

    spin_lock(&pagecache_lock);

    list_del(&page->list);
    list_add(&page->list, &mapping->dirty_pages);
    spin_unlock(&pagecache_lock);

    mark_inode_dirty_pages(mapping->host);
*/
}

void set_page_dirty(struct page *page)
{
    if (!test_and_set_bit(PG_dirty, (unsigned char *)&page->flags))
        __set_page_dirty(page);
}

/* 
 *  释放一个页面及其交换缓存,此处只实现释放页面
 */
void free_page_and_swap_cache(struct page *page)
{
    page_cache_release(page);
}

#define PTE_TABLE_MASK ((PTRS_PER_PTE - 1) * sizeof(pte_t))
#define PMD_TABLE_MASK ((PTRS_PER_PMD - 1) * sizeof(pmd_t))

/*
 * 复制一个进程的vm area到另一个进程
 */
int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma)
{
    pgd_t *src_pgd, *dst_pgd;
    unsigned char need_copy = 0;
    unsigned long address = vma->vm_start; // 获取VMA的起始地址
    unsigned long end = vma->vm_end; // 获取VMA的结束地址
    unsigned long cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE; // 判断是否为COW（写时复制）映射：可写且非共享

    // 获取源和目标进程的PGD（页全局目录）指针，并预先减1
    src_pgd = pgd_offset(src, address) - 1;
    dst_pgd = pgd_offset(dst, address) - 1;

    for (;;)
    {
        pmd_t *src_pmd, *dst_pmd;

        // 移动到下一个PGD条目
        src_pgd++;
        dst_pgd++;

        /* 复制pmd range */

         // 如果源PGD条目为空，跳过整个PMD范围
        if (pgd_none(*src_pgd))
            goto skip_copy_pmd_range;

        // 如果源PGD条目损坏，报告错误并清除
        if (pgd_bad(*src_pgd))
        {
            pgd_ERROR(*src_pgd); // 打印PGD错误信息
            pgd_clear(src_pgd); // 清除损坏的PGD条目

skip_copy_pmd_range:
            address = (address + PGDIR_SIZE) & PGDIR_MASK;  // 对齐到下一个PGD边界
            if (!address || (address >= end)) // 如果地址为0或超出范围，退出
                goto out;

            continue; // 继续下一个PGD
        }

        // 如果目标PGD条目为空，分配新的PMD
        if (pgd_none(*dst_pgd))
        {
            if (!pmd_alloc(dst_pgd, 0))
                goto nomem; // 内存分配失败
        }

        // 获取对应的PMD（页中间目录）指针
        src_pmd = pmd_offset(src_pgd, address);
        dst_pmd = pmd_offset(dst_pgd, address);

        // 循环处理PMD中的所有PTE表
        do
        {
            pte_t *src_pte, *dst_pte;

            /* 复制pte range */

            // 如果源PMD条目为空，跳过整个PTE范围
            if (pmd_none(*src_pmd))
                goto skip_copy_pte_range;

            // 如果源PMD条目损坏，报告错误并清除
            if (pmd_bad(*src_pmd))
            {
                pmd_ERROR(*src_pmd); // 打印PMD错误信息
                pmd_clear(src_pmd); // 清除损坏的PMD条目

skip_copy_pte_range:
                address = (address + PMD_SIZE) & PMD_MASK; // 对齐到下一个PMD边界
                if (address >= end)
                    goto out;  // 如果超出范围，退出

                goto cont_copy_pmd_range; // 继续下一个PMD
            }

            // 如果PMD是段映射（section mapping），跳过整个PMD
            if  (pmd_section(*src_pmd))
            {
                address = (address + PMD_SIZE) & PMD_MASK;
                if (address >= end)
                    goto out;

                goto cont_copy_pmd_range; // 继续下一个PMD
            }

            // 如果目标PMD条目为空，分配新的PTE表
            if (pmd_none(*dst_pmd))
            {
                if (!pte_alloc(dst_pmd, 0))
                    goto nomem;
            }

             // 获取对应的PTE（页表条目）指针
            src_pte = pte_offset(src_pmd, address);
            dst_pte = pte_offset(dst_pmd, address);

            // 循环处理PTE表中的所有条目
            do
            {
                pte_t pte = *src_pte;
                struct page *ptepage;

                /* 复制 one pte */

                // 如果PTE为空，跳过
                if (pte_none(pte))
                    goto cont_copy_pte_range_noset;

                // 如果PTE不在内存中（可能是交换条目）
                if (!pte_present(pte))
                {
                    //swap_duplicate(pte_to_swp_entry(pte));
                    goto cont_copy_pte_range; // 继续处理
                }

                // 获取PTE对应的物理页面
                ptepage = pte_page(pte);
                // 检查页面是否有效且不是保留页面
                if ((!VALID_PAGE(ptepage)) ||
                    PageReserved(ptepage))
                    goto cont_copy_pte_range;

                // 如果是COW映射，在父进程和子进程中都要写保护
                if (cow)
                {
                    ptep_set_wrprotect(src_pte);
                    pte = *src_pte;
                    need_copy = 1;
                }

                // 如果是共享映射，在子进程中标记为clean
                if (vma->vm_flags & VM_SHARED)
                    pte = pte_mkclean(pte); // 标记页面为clean
                pte = pte_mkold(pte); // 标记页面为未被访问
                get_page(ptepage); // 增加页面引用计数

cont_copy_pte_range:
                set_pte(dst_pte, pte);  // 设置目标PTE
                if (need_copy)  // 如果需要COW处理，触发缺页异常
                {
                    handle_mm_fault(dst, vma, address, 1);
                    need_copy = 0; // 重置标志
                }
cont_copy_pte_range_noset:
                address += PAGE_SIZE; // 移动到下一页
                if (address >= end) // 检查是否完成
                    goto out;

                src_pte++; // 下一个源PTE
                dst_pte++; // 下一个目标PTE
            } while ((unsigned long)src_pte & PTE_TABLE_MASK);  // 检查PTE表边界

cont_copy_pmd_range:
            src_pmd++; // 下一个源PMD
            dst_pmd++; // 下一个目标PMD
        } while ((unsigned long)src_pmd & PMD_TABLE_MASK); // 检查PMD表边界
    }
out:
    return 0; // 成功返回

nomem:
    return -ENOMEM;
}

static int free_pte(pte_t pte)
{
    // 检查PTE是否指向一个存在的页面（在内存中）
    if (pte_present(pte))
    {
        // 获取PTE对应的物理页面结构
        struct page *page = pte_page(pte);
        if ((!VALID_PAGE(page)) || PageReserved(page))
            return 0; // 无效或保留页面，不释放，返回0

        // 如果PTE被标记为脏且页面有映射（可能在交换缓存中）
        if (pte_dirty(pte) && page->mapping)
            set_page_dirty(page); // 将页面标记为脏

        // 释放页面并处理交换缓存
        free_page_and_swap_cache(page);
        return 1;
    }
        
    // 处理不在内存中的页面(交换条目),则释放交换条目，减少交换空间的引用计数
    //swap_free(pte_to_swp_entry(pte));
    return 0;
}

/*
 * 清除页中间目录(PMD)项对应的页表项(PTE)范围
 * mm：内存管理结构，虽然函数内未直接使用，但可能用于扩展
 * pmd：指向页中间目录项的指针
 * address：要清除的起始虚拟地址
 * size：要清除的内存区域大小（字节）
 */
static int zap_pte_range(struct mm_struct *mm, pmd_t *pmd, unsigned long address, unsigned long size)
{
    pte_t *pte;
    int freed; // 记录释放的页面数量

    // 检查PMD项是否为空
    if (pmd_none(*pmd))
        return 0; // 如果为空，直接返回0（没有释放任何页面）

    // 检查PMD项是否损坏
    if (pmd_bad(*pmd))
    {
        pmd_ERROR(*pmd);
        pmd_clear(pmd);
        return 0;
    }

    pte = pte_offset(pmd, address);  // 获取对应地址的PTE指针
    address &= ~PMD_MASK; // 将地址对齐到PMD边界（清除低PMD_SIZE位）

    // 确保要清除的范围不超过当前PMD的范围
    if (address + size > PMD_SIZE)
        size = PMD_SIZE - address;
    size >>= PAGE_SHIFT; // 将字节大小转换为页面数量
    freed = 0; // 初始化释放计数器

     // 开始循环清除PTE
    for (;;)
    {
        pte_t page;

        // 检查是否已处理完所有页面
        if (!size)
            break;

        // 原子性地获取并清除PTE
        page = ptep_get_and_clear(pte);
        pte++;  // 移动到下一个PTE
        size--; // 减少剩余页面计数

        // 如果PTE本来就是空的，继续处理下一个
        if (pte_none(page))
            continue;
        
        // 调用free_pte释放页面，并累加释放计数
        freed += free_pte(page);
    }

    return freed;  // 返回释放的页面总数
}

/*
 * 清除页全局目录(PGD)项对应的页中间目录(PMD)范围
 */
static int zap_pmd_range(struct mm_struct *mm, pgd_t *dir, unsigned long address, unsigned long size)
{
    pmd_t *pmd;
    unsigned long end; // 结束地址
    int freed; // 释放的页面总数

    // 检查PGD项是否为空
    if (pgd_none(*dir))
        return 0;
    
    // 检查PGD项是否损坏
    if (pgd_bad(*dir))
    {
        pgd_ERROR(*dir);
        pgd_clear(dir);
        return 0;
    }

    pmd = pmd_offset(dir, address); // 获取对应地址的PMD指针
    address &= ~PGDIR_MASK; // 将地址对齐到PGD边界
    end = address + size; // 计算结束地址
    if (end > PGDIR_SIZE) // 确保结束地址不超过当前PGD的范围
        end = PGDIR_SIZE;
    freed = 0; // 初始化释放计数器

    // 循环处理PMD范围内的所有PMD项
    do
    {
        freed += zap_pte_range(mm, pmd, address, end - address); // 调用zap_pte_range清除当前PMD对应的PTE范围
        address = (address + PMD_SIZE) & PMD_MASK;  // 移动到下一个PMD边界
        pmd++; // 移动到下一个PMD项 
    } while (address < end); // 检查是否处理完整个范围

    return freed;  // 返回释放的页面总数
}

/*
 * 移除给定范围内的用户页面
 */
void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size)
{
    pgd_t *dir;  // 页全局目录指针
    unsigned long end = address + size; // 计算结束地址
    int freed = 0; // 总共释放的页面数

    // 获取起始地址对应的PGD指针
    dir = pgd_offset(mm, address);

    // 检查地址范围有效性
    if (address >= end)
        BUG();
    
    // 获取内存管理结构的页表锁
    spin_lock(&mm->page_table_lock);

    // 循环处理PGD范围内的所有PGD项
    do
    {
        freed += zap_pmd_range(mm, dir, address, end - address); // 调用zap_pmd_range清除当前PGD对应的PMD范围
        address = (address + PGDIR_SIZE) & PGDIR_MASK; // 移动到下一个PGD边界
        dir++; // 移动到下一个PGD项
    } while (address && (address < end)); // 检查地址有效且未超出范围

    // 释放页表锁
    spin_unlock(&mm->page_table_lock);
    
    // 更新内存统计信息
    if (mm->rss > freed)
        mm->rss -= freed; // 减少驻留集大小
    else
        mm->rss = 0; // 如果释放的页面数大于当前rss，重置为0
}

void establish_pte(struct vm_area_struct * vma, unsigned long address, pte_t *page_table, pte_t entry)
{
	set_pte(page_table, entry);
	//flush_tlb_page(vma, address);
	//update_mmu_cache(vma, address, entry);
}


static int is_page_shared(struct page *page)
{
    return 0;
}

void copy_cow_page(struct page * from, struct page * to, unsigned long address)
{
    void *src = page_address(from);
    void *dst = page_address(to);

    memcpy(dst, src, PAGE_SIZE);
}

void break_cow(struct vm_area_struct *vma, struct page *old_page, struct page *new_page, unsigned long address, pte_t *page_table)
{
    copy_cow_page(old_page, new_page, address);
    //flush_page_to_ram(new_page);
	//flush_cache_page(vma, address);
	establish_pte(vma, address, page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
}

/*
 *  1. 次要缺页 (Minor Fault)
 * 含义：页面已经在物理内存中，只需要建立页表映射
 * 场景：
 * COW写时复制成功处理
 * 页面在缓存中但未映射到当前进程
 * 权限调整导致的缺页
 * 特点：处理速度快，不需要磁盘I/O
 *
 *  2. 主要缺页 (Major Fault)
 * 含义：页面不在物理内存中，需要从磁盘加载
 * 场景：
 * 页面在交换分区中
 * 第一次访问文件映射的页面
 * 特点：处理速度慢，涉及磁盘I/O
 *
 *  3. 无效缺页 (Invalid Fault)
 * 含义：真正的错误情况
 * 场景：
 * 访问未映射的地址
 * 权限不足
 * 页面损坏
 * 处理：发送SIGSEGV信号终止进程
 */
static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, pte_t *page_table, pte_t pte)
{
    struct page *old_page, *new_page;

    // 获取PTE对应的物理页面
    old_page = pte_page(pte);
    if (!VALID_PAGE(old_page)) // 检查页面是否有效
        goto bad_wp_page; // 无效页面，跳转到错误处理

    switch (page_count(old_page)) // 检查页面的引用计数
    {

    /*
     * 页面有两个引用，需要进一步分析引用来源:
     * 1 进程A + 进程B：两个进程共享这个页面 → 需要COW复制
     * 2 进程 + 交换缓存：页面在交换缓存中但未被其他进程共享 → 可以重用
     * 3 其他内核引用：如文件系统缓存等
     * 
     * 当页面引用计数为2且满足以下条件时可以重用：
     * 1 一个引用来自当前进程的页表映射
     * 2 另一个引用来自交换缓存（swap cache）
     * 3 且没有其他进程共享这个页面
     */
    case 2: // 引用计数为2的特殊情况
        // 如果页面不在交换缓存中或无法获取锁，跳出switch
        if (!PageSwapCache(old_page) || TryLockPage(old_page))
            break;
        
        // 检查页面是否被共享
        if (is_page_shared(old_page))
        {
            UnlockPage(old_page); // 解锁页面
            break; // 页面被共享，需要复制
        }

        UnlockPage(old_page); // 解锁页面
        //没有break，穿透到case 1

    /* 
     * 页面只有一个引用,表示：
     * 这个物理页面当前只被一个进程映射
     * 没有其他用户在使用这个页面
     * 页面不在交换缓存中
     */
    case 1: // 引用计数为1，可以直接重用页面
        // 刷新缓存页面
        //flush_cache_page(vma, address);
        establish_pte(vma, address, page_table, pte_mkyoung(pte_mkdirty(pte_mkwrite(pte)))); // 建立新的PTE：标记为可写、脏、年轻
        spin_unlock(&mm->page_table_lock); // 释放页表锁
        return 1; // 次要缺页 (Minor Fault)
    }

    // 需要复制页面以实现写时复制（COW）

    spin_unlock(&mm->page_table_lock); // 先释放锁以进行页面分配
    new_page = (struct page *)page_cache_alloc();  // 从页面缓存分配新页面
    if (!new_page)
        return -1;  // 分配失败
    
    spin_lock(&mm->page_table_lock); // 重新获取页表锁

    // 新检查PTE,因为之前释放了锁, 可能有其他线程修改了页表
    if (pte_same(*page_table, pte))
    {
        // 如果旧页面是保留页面，增加RSS计数
        if (PageReserved(old_page))
            ++mm->rss;

        break_cow(vma, old_page, new_page, address, page_table); // 执行COW：复制页面内容并建立新映射
        new_page = old_page; // 释放旧页面
    }

    spin_unlock(&mm->page_table_lock);  // 释放页表锁
    page_cache_release(new_page); // 释放页面（可能是新分配的页面，也可能是旧页面）
    return 1;  // 次要缺页 (Minor Fault)

bad_wp_page: // 错误处理标签
    spin_unlock(&mm->page_table_lock);
    printk("do_wp_page: bogus page at address %08lx (page 0x%lx)\n", address, (unsigned long)old_page);
    return -1; // 返回错误
}

/*
 * 处理匿名页缺页
 */
static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, pte_t *page_table, int write_access, unsigned long addr)
{
    struct page *page = NULL;
    
    // 创建只读的PTE条目，映射到零页面（全零的共享页面）
    pte_t entry = pte_wrprotect(mk_pte(ZERO_PAGE(addr), vma->vm_page_prot));

    // 如果是写访问，需要分配新的物理页面
    if (write_access)
    {
        /*
         * 从高端内存区域分配一个页面
         * 32位系统
         * 内核内存：通常使用低端内存，因为内核需要快速、直接的访问
         * 用户内存：使用高端内存，因为：
         *            用户空间页面不需要永久的内核映射
         *            可以更好地利用大内存系统
         *            避免耗尽低端内存影响内核运行
         * 64位系统
         * 没有严格的低端/高端内存划分，但仍然根据内存使用情况和性能考虑进行分配
         */
        page = (struct page *)alloc_page(GFP_HIGHUSER);
        if (!page)
            return -1;

        // clear_user_highpage(page, addr); // 清空页面内容
        entry = pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))); // 创建可写、脏的PTE条目，映射到新分配的页面
        mm->rss++;  // 增加rss大小统计
        //flush_page_to_ram(page); // 刷新缓存
    }

    set_pte(page_table, entry);  // 设置页表条目
    
    //update_mmu_cache(vma, addr, entry); // 更新MMU缓存
    return 1; // 次要缺页 
}

/*
 * 尝试创建一个新的页面映射,且尽量使用共享方式
 * 如果"write_access"参数为真，则会创建单独的副本以避免下一次缺页
 * 由于这只针对当前不存在的页面调用，不需要刷新旧的虚拟缓存或TLB
 */
static int do_no_page(struct mm_struct *mm, struct vm_area_struct *vma,
                      unsigned long address, int write_access, pte_t *page_table)
{
    struct page *new_page;
    pte_t entry;

    // 如果VMA没有定义操作集或没有nopage操作，则处理为匿名页面
    if (!vma->vm_ops || !vma->vm_ops->nopage)
        return do_anonymous_page(mm, vma, page_table, write_access, address);

    // 调用VMA的nopage操作以获取页面
    new_page = vma->vm_ops->nopage(vma, address & PAGE_MASK, (vma->vm_flags & VM_SHARED) ? 0 : write_access);

    // 检查nopage操作的返回值
    if (new_page == NULL) // 页面不存在
        return 0;
    if (new_page == NOPAGE_OOM) // 内存不足
        return -1;
    ++mm->rss; // 增加 rss
    
    //flush_page_to_ram(new_page);
    //flush_icache_page(vma, new_page);
    entry = mk_pte(new_page, vma->vm_page_prot);  // 创建PTE条目
    // 根据访问类型设置PTE权限
    if (write_access)
        entry = pte_mkwrite(pte_mkdirty(entry)); // 写访问：设置为可写和脏
    else if (page_count(new_page) > 1 && !(vma->vm_flags & VM_SHARED)) // 只读访问且页面有多个引用且非共享：写保护
        entry = pte_wrprotect(entry);

    set_pte(page_table, entry);  // 设置页表条目
    
    //update_mmu_cache(vma, address, entry); //不需要无效化TLB：一个不存在的页面不应该被缓存
    return 2;  // 主要缺页
}


/*
 handle_pte_fault()
    |
 检查PTE状态
    |----页面不存在 (!pte_present)
    |    |---- 全新映射 (pte_none) -> do_no_page()
    |    |---- 在交换空间 -> do_swap_page()
    |
    |----页面存在 (pte_present)
         |----写访问 -> do_wp_page()  // 处理COW
         |----读访问 -> 更新访问位，返回次要缺页
 */
static inline int handle_pte_fault(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, int write_access, pte_t *pte)
{
    pte_t entry;

    // 页表锁来与kswapd同步以及进行SMP安全的原子PTE更新
    spin_lock(&mm->page_table_lock);
    entry = *pte;  // 读取当前的PTE值

     // 检查PTE是否表示页面存在于内存中
    if (!pte_present(entry))
    {
        spin_unlock(&mm->page_table_lock); // 如果它确实不存在，我们知道kswapd和PTE更新之后不会触及它, 所以释放锁

        // 如果PTE为空（完全没有映射）
        if (pte_none(entry))
            return do_no_page(mm, vma, address, write_access, pte);

        // 处理交换页面（页面在交换空间中）
        //return do_swap_page(mm, vma, address, pte, pte_to_swp_entry(entry), write_access);
    }

     // 页面存在内存中，处理写访问
    if (write_access)
    {
        // 如果页面不可写，需要进行写时复制
        //if (!pte_write(entry))
            return do_wp_page(mm, vma, address, pte, entry);

        entry = pte_mkdirty(entry); // 如果页面可写，标记为脏
    }

    entry = pte_mkyoung(entry); // 标记页面为年轻（最近被访问）
    establish_pte(vma, address, pte, entry);   // 建立或更新PTE映射
    
    spin_unlock(&mm->page_table_lock);
    return 1; // 返回次要缺页
}

/*
 * 处理内存管理缺页异常, 此函数必须在执有mm信号量的情况下调用
 */
int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, int write_access)
{
    int ret = -1;
    pgd_t *pgd;
    pmd_t *pmd;

    pgd = pgd_offset(mm, address);
    pmd = pmd_alloc(pgd, address);

    // 检查pmd
    if (pmd)
    {
        pte_t *pte = pte_alloc(pmd, address); // 获取或分配PTE
        if (pte)
            ret = handle_pte_fault(mm, vma, address, write_access, pte);
    }
    return ret <= 0 ? -ENOMEM : 0;;
}

/*
 * 2.x版本linux内核预分配页面函数，在gup.c中已实现mm_populate，此函数后续会删除
 */
int make_pages_present(unsigned long addr, unsigned long end)
{
    int write;
    struct mm_struct *mm = current->mm;
    struct vm_area_struct *vma;

    vma = find_vma(mm, addr);
    write = (vma->vm_flags & VM_WRITE) != 0;
    if (addr >= end)
        BUG();
    do
    {
        if (handle_mm_fault(mm, vma, addr, write) < 0)
            return -1;
        addr += PAGE_SIZE;
    } while (addr < end);

    return 0;
}

/*
 * 获取normal页面结构
 */
struct page *vm_normal_page(struct vm_area_struct *vma, unsigned long address, pte_t pte)
{
    unsigned long pfn = pte_pfn(pte); 

    if (vma->vm_flags & (VM_PFNMAP | VM_MIXEDMAP))
    {

    }

    if (is_zero_pfn(pfn))
        return NULL;

    return pfn_to_page(pfn);
}